package com.fernfx.xingtan.common.network;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;

import com.fernfx.xingtan.common.base.BaseApplication;
import com.fernfx.xingtan.common.network.entity.NetworkErrorResult;
import com.fernfx.xingtan.common.network.entity.NetworkOKResult;
import com.fernfx.xingtan.common.entity.user.MemberEntity;
import com.fernfx.xingtan.user.AccountManager;
import com.fernfx.xingtan.utils.FastJsonUtil;
import com.fernfx.xingtan.utils.LogUtils;
import com.fernfx.xingtan.utils.UIUtil;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * OkHttpProxy
 */

public class OkHttpProxy implements ISJJNetworkProxy {
    private static final String Tag = OkHttpProxy.class.getSimpleName();

    //region 私有变量声明区域
    private static final int OKHTTP_CONNECT_TIME_OUT = 30 * 1000;
    private static final int OKHTTP_READ_TIME_OUT = 30 * 1000;
    private static final int OKHTTP_WRITE_TIME_OUT = 30 * 1000;

    private static final String CONTENT_TYPE_TEXT = "application/json;charset=utf-8";
    private static final MediaType CONTENT_TYPE = MediaType.parse(CONTENT_TYPE_TEXT);
    private static final MediaType IMAGETYPE = MediaType.parse("image/png");

    private static final int OKHTTP_FAILURE = -1000;
    private static final int OKHTTP_FAILURE_TAG = 0;
    private static final int OKHTTP_SUCCESS_TAG = 1;
    private static final int OKHTTP_NETWORK_TAG = 2;

    private static final String OKHTTP_ERROR_MESSAGE_KEY = "error_message";
    private static final String OKHTTP_SUCCESS_MESSAGE_KEY = "success_message";

    private Call mCurrentCall;

    //单位是秒
    private int mMaxAge = 0;

    private boolean mAlreadyEncode = false;

    private Map<String, String> mHeaders = new HashMap<>();
    private Map<String, String> mQueries = new HashMap<>();

    private static OkHttpClient sOkHttpClient;
    //endregion

    static {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .cache(new Cache(UIUtil.getContext().getCacheDir(), 10 * 1024 * 1024))
                .connectTimeout(OKHTTP_CONNECT_TIME_OUT, TimeUnit.MILLISECONDS)
                .readTimeout(OKHTTP_READ_TIME_OUT, TimeUnit.MILLISECONDS)
                .writeTimeout(OKHTTP_WRITE_TIME_OUT, TimeUnit.MILLISECONDS)
                .hostnameVerifier(new HostnameVerifier() {
                    @SuppressLint("BadHostnameVerifier")
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                });

        sOkHttpClient = builder.build();
    }

    private void addHeaders(Request.Builder builder, Map<String, String> headers) {
        if (mHeaders.size() > 0) {
            for (Map.Entry<String, String> entry : mHeaders.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
                    builder.addHeader(key, value);
                }
            }
        }

        if (null != builder && null != headers) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                String key = entry.getKey();
                if (!mHeaders.containsKey(key)) {
                    String value = entry.getValue();
                    if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
                        builder.addHeader(key, value);
                    }
                }
            }
        }
    }

    private void addQueries(HttpUrl.Builder builder, Map<String, String> queries) {
        if (mQueries.size() > 0) {
            doSortQueries(builder, mQueries);
        }
        if (null != builder && null != queries) {
            doSortQueries(builder, queries);
        }
    }

    /**
     * 对query 字典排序
     *
     * @param builder request
     * @param queries data
     */
    private void doSortQueries(HttpUrl.Builder builder, Map<String, String> queries) {
        List<String> keyList = new ArrayList<>();
        for (Map.Entry<String, String> entry : queries.entrySet()) {
            String key = entry.getKey();
            keyList.add(key);
        }
        //对key键值按字典升序排序
        Collections.sort(keyList);

        for (String key : keyList) {
            String value = queries.get(key);
            if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
                if (mAlreadyEncode) {
//                    builder.setEncodedQueryParameter(key, value);
                    builder.addEncodedQueryParameter(key, value);
                } else {
                    builder.setQueryParameter(key, value);
//                    builder.addQueryParameter(key, value);
                }
            }
        }
    }

    private Request.Builder getRequestBuilder(String url, Map<String, String> headers, Map<String, String> queries) {
        HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder();
        addQueries(httpBuilder, queries);

        Request.Builder builder = new Request.Builder().url(httpBuilder.build());
        if (null == headers) {
            headers = new HashMap<>(1);
            headers.put("Accept",CONTENT_TYPE_TEXT);
        }else{
            headers.put("Accept",CONTENT_TYPE_TEXT);
        }
        //如果是登录后请求头增加token
        if (AccountManager.isExistToken()) {
            MemberEntity memberEntity = AccountManager.getUserInfo();
            headers.put("token",memberEntity.getObj().getToken());
        }
        addHeaders(builder, headers);
        CacheControl.Builder cacheBuilder = new CacheControl.Builder();
        cacheBuilder.maxAge(mMaxAge, TimeUnit.SECONDS);
        if (mMaxAge == 0) {
            cacheBuilder.noCache();
        }
        builder.cacheControl(cacheBuilder.build());
        return builder;
    }


    //region ISJJNetworkRequestProxy
    @Override
    public ISJJNetworkProxy addHeader(String name, String value) {
        if (!mHeaders.containsKey(name)) {
            mHeaders.put(name, value);
        }
        return null;
    }

    @Override
    public ISJJNetworkProxy addQueryParam(String name, String value) {
        if (!mQueries.containsKey(name)) {
            mQueries.put(name, value);
        }
        return this;
    }

    @Override
    public ISJJNetworkProxy setMaxAge(int maxAge) {
        mMaxAge = maxAge;
        return this;
    }

    @Override
    public ISJJNetworkProxy alreadyEncode(boolean alreadyEncode) {
        mAlreadyEncode = alreadyEncode;
        return this;
    }

    @Override
    public void get(String url, Map<String, String> headers, IRequestCallback callback) {
        get(url, headers, null, callback);
    }

    @Override
    public void get(String url, Map<String, String> headers, Map<String, String> queries, final IRequestCallback callback) {
        Request request = getRequestBuilder(url, headers, queries).build();
        sendRequest(request, callback);
    }


    @Override
    public void post(String url, Map<String, String> headers, String jsonString, IRequestCallback callback) {
        post(url, headers, jsonString, null, callback);
    }

    @Override
    public void post(String url, Map<String, String> headers, String jsonString, Map<String, String> queries, IRequestCallback callback) {
        Request.Builder builder = getRequestBuilder(url, headers, queries);
        if (!TextUtils.isEmpty(jsonString)) {
            RequestBody body = RequestBody.create(CONTENT_TYPE, jsonString);
            builder.post(body);
        } else {
            builder.post(RequestBody.create(CONTENT_TYPE, ""));
        }
        Request request = builder.build();
        sendRequest(request, callback);
    }

    @Override
    public void putImage(String url, Map<String, String> headers, File file, IRequestCallback callback) {
        RequestBody fileBody = RequestBody.create(IMAGETYPE, file);
        MultipartBody body = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", file.getName(), fileBody)
                .build();

        Request.Builder builder = new Request.Builder()
                .url(url)
                .post(body);
        addHeaders(builder, headers);
        Request request = builder.build();
        sendRequest(request, callback);
    }


    @Override
    public void cancel() {
        if (null != mCurrentCall
                && !mCurrentCall.isCanceled()) {
            mCurrentCall.cancel();
        }
    }
    //endregion

    //region 实际发送网络请求的接口

    /**
     * 实际发送网络请求的接口
     *
     * @param request  请求
     * @param callback 回调
     */
    private void sendRequest(final Request request, final IRequestCallback callback) {
        Request.Builder builder = new Request.Builder()
                .method(request.method(), request.body())
                .url(request.url())
                //统一header头
                .headers(request.headers());

        if (mMaxAge > 0) {
            CacheControl cc = new CacheControl.Builder()
                    .maxAge(mMaxAge, TimeUnit.SECONDS)
                    .build();
            builder.cacheControl(cc);
        } else {
            CacheControl cc = new CacheControl.Builder().noCache().build();
            builder.cacheControl(cc);
        }
        mCurrentCall = sOkHttpClient.newCall(builder.build());
        mCurrentCall.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                mMaxAge = 0;
                if (null != callback) {
                    Message message = Message.obtain();
                    message.obj = callback;
                    message.what = OKHTTP_NETWORK_TAG;

                    NetworkErrorResult errorResult = new NetworkErrorResult(e.getMessage(), OKHTTP_FAILURE, null);
                    Bundle bundle = new Bundle();
                    bundle.putSerializable(OKHTTP_ERROR_MESSAGE_KEY, errorResult);
                    message.setData(bundle);
                    mHandler.sendMessage(message);
                    mAlreadyEncode = false;
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Message message = Message.obtain();
                message.obj = callback;
                mAlreadyEncode = false;
                mMaxAge = 0;

                if (null != callback) {
                    ResponseBody body = response.body();
                    String result = (null != body) ? body.string() : null;
                    if (response.isSuccessful()) {
                        message.what = OKHTTP_SUCCESS_TAG;
                        NetworkOKResult okResult = new NetworkOKResult(result, response.headers());
                        okResult.setNetworkRequest(response.networkResponse() != null);
                        Bundle bundle = new Bundle();
                        bundle.putSerializable(OKHTTP_SUCCESS_MESSAGE_KEY, okResult);
                        message.setData(bundle);
                    } else {
                        message.what = OKHTTP_FAILURE_TAG;
                        Bundle bundle = new Bundle();
                        NetworkErrorResult errorResult = new NetworkErrorResult(result, response.code(), response.headers());
                        bundle.putSerializable(OKHTTP_ERROR_MESSAGE_KEY, errorResult);
                        message.setData(bundle);
                    }
                    Map<String, String> logs = new HashMap<>();
                    logs.put("request url", request.url().toString());
                    logs.put("cache respones", null == response.cacheResponse() ? "null" : response.cacheResponse().toString());
                    logs.put("network respones", null == response.networkResponse() ? "null" : response.networkResponse().toString());
                    logs.put("result", TextUtils.isEmpty(result) ? "null" : result);
                    LogUtils.d("OkHttpProxy", "sendRequest.onResponse:\t\t"+ FastJsonUtil.BeanToJson(logs));
                    mHandler.sendMessage(message);
                }
            }
        });
    }

    private Handler mHandler = new OkHttpResultHandler(BaseApplication.getInstance().getApplicationContext().getMainLooper());

    static class OkHttpResultHandler extends Handler {
        OkHttpResultHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case OKHTTP_FAILURE_TAG: {
                    IRequestCallback callback = (IRequestCallback) msg.obj;
                    Bundle bundle = msg.getData();
                    if (null != callback && null != bundle) {
                        NetworkErrorResult errorResult = (NetworkErrorResult) bundle.getSerializable(OKHTTP_ERROR_MESSAGE_KEY);
                        if (null != errorResult) {
//                            ApiStatusUtil.getInstance().apiStatusError(errorResult.getErrorMessage(), errorResult.getErrorCode());
                            try {
                                callback.onFailure(errorResult.getErrorMessage(), errorResult.getErrorCode());
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                break;
                case OKHTTP_SUCCESS_TAG: {
                    IRequestCallback callback = (IRequestCallback) msg.obj;
                    Bundle bundle = msg.getData();
                    NetworkOKResult okResult = (NetworkOKResult) bundle.getSerializable(OKHTTP_SUCCESS_MESSAGE_KEY);
                    if (null != callback
                            && bundle.containsKey(OKHTTP_SUCCESS_MESSAGE_KEY)) {
                        try {
                            callback.onSuccessful(okResult);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                break;
                case OKHTTP_NETWORK_TAG: {
                    IRequestCallback callback = (IRequestCallback) msg.obj;
                    Bundle bundle = msg.getData();
                    NetworkErrorResult errorResult = (NetworkErrorResult) bundle.getSerializable(OKHTTP_ERROR_MESSAGE_KEY);
                    if (null != callback) {
                        if (null != errorResult && null != errorResult.getErrorMessage()) {
                            callback.onNetworkError(errorResult.getErrorMessage(),
                                    errorResult.getErrorCode());
                        } else {
                            callback.onNetworkError("网络请求错误",
                                    -200);
                        }
                    }
                }
                break;
                default:
                    break;
            }
            super.handleMessage(msg);
        }
    }
}
